#include "stdafx.h"

//#include <atlbase.h>
#include <oledbdm.h>

#include "DMAlgorithm.h"
#include "DMAttributeSet.h"
#include "DMAlgorithmTrainPredict.h"
using namespace PluginAlgorithms;


System::Boolean	 PushCaseSet::IsDense()
{
	System::Boolean ret = false;
	USES_ERROR_HANDLING;
	
	THROW_ON_FAILURE( m_wpCaseSet->IsDense() );
	ret = (CURRENT_STATUS == S_OK) ? true : false;
	
	return ret;
}


void PushCaseSet::StartCases(ICaseProcessor^ caseProcessor )
{
	USES_ERROR_HANDLING;
	AutoUseContext	apEC;

	InternalCaseProcessor^	mCaseProcessor = gcnew InternalCaseProcessor(caseProcessor);
	mCaseProcessor->SetDense( IsDense() );
	mCaseProcessor->SetExecutionContext( apEC );

	/////////////////////////////////////////////////////////////
	// Get the IDMCaseProcessor pointer from the managed object
	System::IntPtr wrappedUnk = System::Runtime::InteropServices::Marshal::GetIUnknownForObject( mCaseProcessor );
	IUnknown*	pUnk = (IUnknown*)(INT_PTR)wrappedUnk;
	
	CComPtr<IDMCaseProcessor>	spCaseProc;
	THROW_ON_FAILURE( pUnk->QueryInterface( __uuidof(IDMCaseProcessor), (void**)&spCaseProc) );
	

	THROW_ON_FAILURE( m_wpCaseSet->StartCases(spCaseProc, false) );
}



HRESULT InternalCaseProcessor::ExportedRequestCapacity(
	ULONG in_cCapacity)
{
	// This function should not be called!! 
	return E_FAIL;
}

HRESULT InternalCaseProcessor::ExportedProcessCase(
	DM_CaseID in_CaseID,
	ULONG	in_cAttributes ,
	ULONG*	in_rgValues)
{
	
	AutoPushContext autoCtx(m_wpEC);
	USES_ERROR_HANDLING;

	
	DM_TRY
	{
		DMCaseItr caseiter;
	
		// Initialize the case iterator
		caseiter.SetCase(in_rgValues, in_cAttributes, m_fIsDense);


		// Initialize the managed class that will wrap the case iterator
		m_trainingCase->SetCaseIterator(&caseiter);
		m_trainingCase->MoveFirst();
		m_caseProcessor->ProcessCase( in_CaseID, m_trainingCase);
	}
	DM_CATCHALL
	DM_FINALLY
	{
		m_trainingCase->SetCaseIterator(NULL);
	}
	
	REPORT_STATUS;
}



bool	MiningCase::MoveNext()
{
	if( S_OK == m_wpCaseItr->NextAttr() )
		return true;
	return false;
}
bool	MiningCase::MoveFirst()
{
	if( S_OK == m_wpCaseItr->ResetCase() )
		return true;
	return false;
}



System::UInt32 MiningCase::Attribute::get()
{
	return (System::UInt32)m_wpCaseItr->Att();
}

StateValue ^	MiningCase::Value::get()
{
	StateValue ^ stateVal = gcnew StateValue();
	stateVal->Set( (DM_STATE_VALUE&)m_wpCaseItr->Value() );

	return stateVal;
}

System::UInt32 MiningCase::IndexValue::get()
{
	return (System::UInt32)m_wpCaseItr->IndexVal();
}

System::Double MiningCase::DoubleValue::get()
{
	return (System::Double)m_wpCaseItr->DoubleVal();
}

StateValue ^	MiningCase::FindGetValue(System::UInt32 att)
{
	StateValue ^ stateVal = gcnew StateValue();
	stateVal->Set( (DM_STATE_VALUE&)m_wpCaseItr->ValFromAttribute((DM_Attribute)att) );

	return stateVal;
}








PredictionResult::PredictionResult(	DM_PREDICTION_FLAGS	in_Flags, 
					IDMAttributeGroup*	in_wpAttributeGroup,
					ULONG				in_ulMaxPredictions,
					ULONG				in_ulMaxStates,
					DM_ATTRIBUTE_VALUE*	in_rgInputAttValues,
					ULONG				in_ulInputAttValues)
{
	USES_ERROR_HANDLING;

	m_flags				=	in_Flags;
	m_wpAttrGroup		=	in_wpAttributeGroup;
	m_ulMaxPredictions	=	in_ulMaxPredictions;
	m_ulMaxStates		=	in_ulMaxStates;


	m_ppPredictions		=	NULL;
	m_pcPredictions		=	0;
	m_fPreAlloc			=	false;
	m_fStatesAlloc		=	false;

	m_rgInputAttValues	=	in_rgInputAttValues;
	m_ulInputAttValues	=	in_ulInputAttValues;

	
	m_mrgOutputAttributes		=	nullptr;

	// Initialize the heap
	long maxSize = m_ulMaxPredictions;
	if( maxSize == (ULONG)-1)
	{
		DM_Attribute	PredAtt	=	DM_UNSPECIFIED;
		THROW_ON_FAILURE( m_wpAttrGroup->Reset() );
		while ((CURRENT_STATUS = m_wpAttrGroup->Next(&PredAtt)) != S_FALSE)
		{
			THROW_ON_FAILURE(CURRENT_STATUS);
			maxSize++;
		}

	}
	m_mHeapAttributeDistributions	=	gcnew AttStatsBinaryHeap(
											maxSize, 
											this->AttributeSortBy,
											this->AttributeSortDirection);


	// Build the vector of attributes that should actually be predicted
	bool fFilterOutput	=	((in_Flags & DMPF_EXCLUDE_INPUT_ATTRIBUTES) || (in_Flags & DMPF_ONLY_INPUT_ATTRIBUTES));
	if( m_ulInputAttValues == 0 || !fFilterOutput )
	{
		// No input or  ignore the flags -- bail out
		return;
		
	}

	// Build array of actual output attributes
	m_mrgOutputAttributes	=	gcnew System::Collections::ArrayList();

	

	DM_Attribute	PredAtt	=	DM_UNSPECIFIED;
	
	THROW_ON_FAILURE( m_wpAttrGroup->Reset() );
	while ((CURRENT_STATUS = m_wpAttrGroup->Next(&PredAtt)) != S_FALSE)
	{
		THROW_ON_FAILURE(CURRENT_STATUS);

		if( PredAtt == DM_UNSPECIFIED )
			continue;

		if( m_flags & DMPF_EXCLUDE_INPUT_ATTRIBUTES )
		{
			ULONG i;
			for( i = 0; i < m_ulInputAttValues; i++)
				if( m_rgInputAttValues[i].Attribute == PredAtt )
					break;
			if( i < m_ulInputAttValues)		// PredAtt found in the list of input cases
				continue;
		}

		if( m_flags & DMPF_ONLY_INPUT_ATTRIBUTES )
		{
			ULONG i;
			for( i = 0; i < m_ulInputAttValues; i++)
				if( m_rgInputAttValues[i].Attribute == PredAtt )
					break;
			if( i == m_ulInputAttValues)		// PredAtt found in the list of input cases
				continue;
		}

		
		m_mrgOutputAttributes->Add( ((System::UInt32)PredAtt));
	}


	


}



void PredictionResult::ErrorCleanup()
{
	AutoUseContext	apEC;


	USES_ERROR_HANDLING;

	CComPtr<IDMMemoryAllocator>	spAlloc;
	THROW_ON_FAILURE(apEC->GetVectorAlloc( &spAlloc ));

	CComPtr<IDMStringHandler>	spStringHandler;
	THROW_ON_FAILURE(apEC->GetStringHandler( &spStringHandler));


	m_flags				=	0;
	m_wpAttrGroup		=	0;
	m_ulMaxPredictions	=	DM_UNSPECIFIED;
	m_ulMaxStates		=	DM_UNSPECIFIED;

	

	m_mrgOutputAttributes	=	nullptr;
	m_mHeapAttributeDistributions = nullptr;

	if( m_fPreAlloc )
	{
		// delete state statistics allocate while predicting, if any
		if( m_fStatesAlloc )
		{
			for( ULONG nIndex = 0; nIndex < *m_pcPredictions; nIndex ++)
			{
				DM_ATTRIBUTE_STAT&	AttStat	=	(*m_ppPredictions)[nIndex];
				THROW_ON_FAILURE( spAlloc->Free( (BYTE*)AttStat.rgStateStats) );
				AttStat.ulStates	=	0;

			}
		}

		return;
	}
	

	for( ULONG nIndex = 0; nIndex < *m_pcPredictions; nIndex ++)
	{
		DM_ATTRIBUTE_STAT&	AttStat	=	(*m_ppPredictions)[nIndex];

		if( AttStat.strNodeID != NULL )
		{
			THROW_ON_FAILURE(  spStringHandler->DestroyStringHandle( AttStat.strNodeID ) );
			AttStat.strNodeID = NULL;
		}
		THROW_ON_FAILURE( spAlloc->Free( (BYTE*)AttStat.rgStateStats) );
		AttStat.ulStates	=	0;
	}

	THROW_ON_FAILURE( spAlloc->Free( (BYTE*)*m_ppPredictions) );
	*m_ppPredictions	=	NULL;

	m_ppPredictions		=	NULL;
	m_ulMaxPredictions	=	0;
}

PredictionResult::InputAttributesTreatment PredictionResult::InputAttributeRestrictions::get()
{
	PredictionResult::InputAttributesTreatment ret = PredictionResult::InputAttributesTreatment::None;
	if( m_flags	& DMPF_EXCLUDE_INPUT_ATTRIBUTES )
	{
		ret = PredictionResult::InputAttributesTreatment::ExcludeInputAttributes;
	}
	else if( m_flags	& DMPF_ONLY_INPUT_ATTRIBUTES )
	{
		ret = PredictionResult::InputAttributesTreatment::PredictOnlyInputAttributes;
	}
	
	return ret;
}

System::Boolean PredictionResult::IncludeMissingState::get()
{
	if (m_flags	& DMPF_EXCLUDE_MISSING_STATE )
	{
		return false;	
	}
	return true;
}

System::Boolean PredictionResult::IncludeStatistics::get()
{
	if (m_flags	& DMPF_INCLUDE_STATS )
	{
		return true;	
	}
	return false;
}

System::Boolean PredictionResult::IncludeNodeId::get()
{
	if (m_flags	& DMPF_INCLUDE_NODE_ID )
	{
		return true;	
	}
	return false;
}

PredictionResult::SortDirection PredictionResult::AttributeSortDirection::get()
{
	if (m_flags	& DMPF_ATTRIBUTE_SORT_ASCENDING )
	{
		return PredictionResult::SortDirection::Ascending;	
	}
	return PredictionResult::SortDirection::Descending;
}

PredictionResult::SortDirection PredictionResult::StateSortDirection::get()
{
	if (m_flags	& DMPF_STATE_SORT_ASCENDING )
	{
		return PredictionResult::SortDirection::Ascending;	
	}
	return PredictionResult::SortDirection::Descending;

}

PredictionResult::SortBy PredictionResult::AttributeSortBy::get()
{
	if( m_flags & DMPF_INCLUDE_STATS )
	{
		if( m_flags & DMPF_ATT_SORT_EXIST_SUPPORT)
		{
			return PredictionResult::SortBy::Support;
		}
		else if ( m_flags & DMPF_ATT_SORT_EXIST_PROB)
		{
			return PredictionResult::SortBy::Probability;
		}
		else if ( m_flags & DMPF_ATT_SORT_EXIST_ADJUST_PROB)
		{
			return PredictionResult::SortBy::AdjustedProbability;
		}
	}
	return PredictionResult::SortBy::None;
}

PredictionResult::SortBy PredictionResult::StateSortBy::get()
{
	if( m_flags & DMPF_INCLUDE_STATS )
	{
		if( m_flags & DMPF_STATE_SORT_SUPPORT)
		{
			return PredictionResult::SortBy::Support;
		}
		else if ( m_flags & DMPF_STATE_SORT_PROB)
		{
			return PredictionResult::SortBy::Probability;
		}
		else if ( m_flags & DMPF_STATE_SORT_ADJUST_PROB)
		{
			return PredictionResult::SortBy::AdjustedProbability;
		}
		else if ( m_flags & DMPF_STATE_SORT_VARIANCE)
		{
			return PredictionResult::SortBy::Variance;
		}
	}
	return PredictionResult::SortBy::None;
}



AttributeGroup ^	PredictionResult::OutputAttributes::get()
{
	if( m_mrgOutputAttributes )
	{
		return gcnew AttributeGroup(m_mrgOutputAttributes);
	}
	else
	{
		return gcnew AttributeGroup(m_wpAttrGroup);
	}
}


void PredictionResult::AddPrediction( AttributeStatistics ^ attStat )
{
	m_mHeapAttributeDistributions->Insert( attStat );
}



void PredictionResult::PostProcess()
{
	AutoUseContext	apEC;
	/*
		// Idea:
			-- 
			1) Extract sorted attribute statistics from binary heap into an array list
			2) if( !DMPF_INCLUDE_STATS ) call ClearStats for all the attributes
			3) Remove attributes that are not in the output set
			4) foreach attribute Sort states <B>ascending</B> based on criterion
			5) Allocate buffers if necessary
			6) Copy into the output buffer the TOP MaxAttrs and the TOP Max States 
			(starting from beginning or end, depeding on the sort direction)
	*/

	int nIndex;

	System::Collections::ArrayList^ mrgAttributeDistributions = gcnew System::Collections::ArrayList();

	// 1) Extract sorted attribute statistics from binary heap into an array list
	do
	{
		AttributeStatistics^ stats = m_mHeapAttributeDistributions->DeleteRoot();
		if( stats != nullptr )
		{
			mrgAttributeDistributions->Insert(0, stats );
		}
		else
			break;
	}while(true);


	// 2) if( !DMPF_INCLUDE_STATS ) call ClearStats for all the attributes
	if( !(m_flags & DMPF_INCLUDE_STATS) )
	{
		
		for( nIndex = 0; nIndex < mrgAttributeDistributions->Count; nIndex ++ )
		{
			AttributeStatistics ^	pAtt	=	dynamic_cast<AttributeStatistics ^>(mrgAttributeDistributions[nIndex]);
			pAtt->ClearStatistics();
		}
	}

	// 3) Remove attributes that are not in the output set
	AttributeGroup ^	pOutputAtt	=	OutputAttributes;

	for( nIndex = 0; nIndex < mrgAttributeDistributions->Count; nIndex ++ )
	{
		AttributeStatistics ^	pAtt	=	dynamic_cast<AttributeStatistics ^>(mrgAttributeDistributions[nIndex]);
		
		pOutputAtt->Reset();
		System::UInt32	nAtt;
		bool	 fFound = false;
		while( pOutputAtt->Next(nAtt) )
		{
			if( nAtt == pAtt->Attribute )
			{
				fFound = true;
				break;
			}
		}
		if( !fFound )
		{
			pAtt = nullptr;
			mrgAttributeDistributions->RemoveAt( nIndex );
			nIndex --;
		}
	}

	// 4) foreach attribute Sort states <B>ascending</B> based on criterion
	if( StateSortBy !=  PredictionResult::SortBy::None )
	{
		StatisticsComparer stateComp( StateSortBy );
		for( nIndex = 0; nIndex < mrgAttributeDistributions->Count; nIndex ++ )
		{
			AttributeStatistics ^	pAtt	=	dynamic_cast<AttributeStatistics ^>(mrgAttributeDistributions[nIndex]);
			pAtt->m_mrgStateStats->Sort((IComparer ^)stateComp);
		}
	}
		
	// 5) Allocate buffers if necessary
	USES_ERROR_HANDLING;
	
	CComPtr<IDMMemoryAllocator>	spAlloc;
	THROW_ON_FAILURE(apEC->GetVectorAlloc( &spAlloc ));

	ULONG	cMaxPredictions	=	0;
	ULONG	cMaxStates		=	0;
	if ( m_fPreAlloc )
	{
		cMaxPredictions	=	min(m_ulMaxPredictions, (ULONG)mrgAttributeDistributions->Count);
	}
	else
	{
		cMaxPredictions	=	(ULONG)mrgAttributeDistributions->Count;

		// Allocate buffer for attrib stats
		THROW_ON_FAILURE( spAlloc->Alloc( cMaxPredictions*sizeof(DM_ATTRIBUTE_STAT), (BYTE**)m_ppPredictions) );
		memset(*m_ppPredictions, 0, sizeof(DM_ATTRIBUTE_STAT) * cMaxPredictions);
	}
	
	*m_pcPredictions	=	cMaxPredictions;
	
	

	// 6) Copy into the output buffer the TOP MaxAttrs and the TOP Max States 
	ULONG	nAttIndex	=	0;

	CComPtr<IDMStringHandler>	spStringHandler;
	THROW_ON_FAILURE(apEC->GetStringHandler( &spStringHandler ));


	for( nAttIndex = 0; nAttIndex < cMaxPredictions; nAttIndex ++ )
	{
		AttributeStatistics ^	pAtt	=	dynamic_cast<AttributeStatistics ^>(mrgAttributeDistributions[nAttIndex]);

		DM_ATTRIBUTE_STAT&	AttStat	=	(*m_ppPredictions)[nAttIndex];
		AttStat.Attribute				=	pAtt->m_attStat.Attribute;
		AttStat.dblMin					=	pAtt->m_attStat.dblMin;
		AttStat.dblMax					=	pAtt->m_attStat.dblMax;
		AttStat.dblExistingSupport		=	pAtt->m_attStat.dblExistingSupport;
		AttStat.dblExistingProbability	=	pAtt->m_attStat.dblExistingProbability;
		AttStat.dblExistingAdjustedProbability	=	pAtt->m_attStat.dblExistingAdjustedProbability;
		
		
		
		if( (pAtt->NodeId != nullptr) )
		{
			THROW_ON_FAILURE( spStringHandler->CreateNewStringHandle( &AttStat.strNodeID ) );
			BSTR bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( pAtt->NodeId );
			
			CURRENT_STATUS = spStringHandler->CopyBufferToHandle( AttStat.strNodeID, bstrTmp, pAtt->NodeId->Length);
			System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*)bstrTmp );
			THROW_ON_FAILURE( CURRENT_STATUS );
		}
			 



		// Handle state statistics
		if( !(m_flags & DMPF_INCLUDE_STATS) )
		{
			AttStat.ulStates	=	0;
		}
		else
		{
			// Allocate state statistics buffer, if needed
			ULONG cMaxStates	=	min( m_ulMaxStates, (ULONG)pAtt->m_mrgStateStats->Count );
			AttStat.ulStates	=	cMaxStates;

			if( !m_fPreAlloc || AttStat.rgStateStats == NULL )
			{
				// Keep track of the fact that states were allocated
				m_fStatesAlloc	=	false;

				// Allocate buffer for attrib stats
				THROW_ON_FAILURE( spAlloc->Alloc( cMaxStates*sizeof(DM_STATE_STAT), (BYTE**)&AttStat.rgStateStats) );
				memset(AttStat.rgStateStats, 0, sizeof(DM_STATE_STAT) * cMaxStates);
			}
			
			
			// Copy the state statistics 
			ULONG	nCurrentState = 0, nEndState = 0;
			ULONG	nStateIndex	=	0;

			if (m_flags	& DMPF_STATE_SORT_ASCENDING )
			{
				nCurrentState	=	0;
				nEndState	=	nCurrentState + cMaxStates;
			}
			else
			{
				nCurrentState	=	pAtt->m_mrgStateStats->Count - 1;
				nEndState		=	nCurrentState - cMaxStates;
			}

			while(nCurrentState != nEndState)
			{
				StateStatistics^		stateStat	=	(StateStatistics^)pAtt->m_mrgStateStats[nCurrentState];
				DM_STATE_STAT&			StateStat	=	AttStat.rgStateStats[nStateIndex];
				pin_ptr<DM_STATE_STAT>	destStat	=	&stateStat->m_StateStat;
				memcpy(&StateStat, destStat, sizeof(DM_STATE_STAT) );

				if (m_flags	& DMPF_STATE_SORT_ASCENDING )
					nCurrentState++;
				else
					nCurrentState--;
				
				// increment the next write index
				nStateIndex ++;
			}
		} // end Allocate state statistics buffer, if needed
	}
}


int	StatisticsComparer::Compare(Object ^	att1, Object ^ att2)
{
	int iRet	=	0;

	double d1 = 0.0, d2 = 0.0;
	StateStatistics^ state1	=	(StateStatistics ^)att1;
	StateStatistics^ state2	=	(StateStatistics ^)att2;
	switch(m_criterion)
	{
	case PredictionResult::SortBy::Support:
			d1	=	state1->Support;
			d2	=	state2->Support;
			break;
	case PredictionResult::SortBy::Probability:
			d1	=	state1->Probability;
			d2	=	state2->Probability;
			break;
		case PredictionResult::SortBy::Variance:
			d1	=	state1->Variance;
			d2	=	state2->Variance;
			break;
		case PredictionResult::SortBy::AdjustedProbability:
			d1	=	state1->AdjustedProbability;
			d2	=	state2->AdjustedProbability;
			break;
	}

	return ( d1 < d2 ) ? -1 :( (d1 > d2 ) ? 1  : 0 );
}